#include"CentralCache.h"
#include"PageCache.h"

CentralCache CentralCache::_sInst;

//获取一个非空的Span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
	Span* it = list.begin();
	while (it != list.end())
	{
		if (it->_freeList != nullptr)    
			return it;
		else
			it = it->_next;           //遇见空span，再往后找
	}

	//先把CentralCache的桶锁解开，这样其它内存释放内存对象回来，不会被阻塞
	list._mtx.unlock();
	//走到这里，说明没有非空的span了，只能找PageCache要了
	PageCache::GetInstance()->_pageMtx.lock();       //加锁
	Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
	span->_isUse = true;              //该span在被使用
	span->objectSize = size;
	PageCache::GetInstance()->_pageMtx.unlock();        //解锁

	//对span的切分不同加锁，因为其他的线程获取不到该span

	//计算span的大块内存的起始地址以及其大小,以及尾部地址
	char* start = (char*)(span->_pageid << PAGE_SHIFT);   
	size_t bytes = span->_n << PAGE_SHIFT;          
	char* end = start + bytes;

	//先把大块内存切成自由链表链接起来
	// #1 先切一块下来做头结点，后续进行尾插（尾插可以保证其地址正向连续，加快申请空间的性能）
 	span->_freeList = start;
	start += size;
	void* tail = span->_freeList;      //单个头结点，tail也在_freelist头部

	while (start < end)     //切块尾插
	{
		NextObj(tail) = start;
		start += size;
		tail = NextObj(tail);
	}

	NextObj(tail) = nullptr;
	list._mtx.lock();     //插入CentralCache中时，要加锁，避免
	list.PushFront(span);         //头插到所需的CentralCache队列

	return span;
}

size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t n, size_t size)
{
	size_t index = SizeClass::Index(size);        //获取桶下标
	_spanLists[index]._mtx.lock();              //对应桶加锁

	Span* span = GetOneSpan(_spanLists[index], size);
	assert(span);
	assert(span->_freeList);

	//从span中获取所需对象
	start = span->_freeList;
	end = start;
	//for (size_t i = 0; i < n-1; i++)
	//{
	//	end = NextObj(end);         //end后移
	//}
	size_t i = 0;
	size_t actualNum = 1;
	while (i < n-1  && NextObj(end)!= nullptr)    //选够要给的，或者不够，把剩下的全给出去
	{
		end = NextObj(end);
		++i;
		++actualNum;
	}
	span->_freeList = NextObj(end);    //将剩余的接在freelist上
	NextObj(end) = nullptr;
	span->_useCount += actualNum;        //计数span中被拿走的个数，释放时减减，来检测

	_spanLists[index]._mtx.unlock();      //解锁
	return actualNum;
}

void CentralCache::ReleaseListToSpans(void* start, size_t size)
{
	size_t index = SizeClass::Index(size);
	_spanLists[index]._mtx.lock();        //归还span时要加锁
	while (start)    
	{
		void* next = NextObj(start);

		Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
		NextObj(start) = span->_freeList;
		span->_freeList = start;
		span->_useCount--;
		//span上的所有内存都回来了，这个span就可以归还给pagecache，并进行也合并；
		if (span->_useCount == 0)
		{
			_spanLists[index].Erase(span);
			span->_freeList = nullptr;
			span->_next = nullptr;
			span->_prev = nullptr;

			_spanLists[index]._mtx.unlock();     //合并page时，与central无关了，解锁

			PageCache::GetInstance()->_pageMtx.lock();       //合并时加pagecache锁
			PageCache::GetInstance()->ReleaseSpanToPageCache(span);
			PageCache::GetInstance()->_pageMtx.unlock();       //合并完成，解锁

			_spanLists[index]._mtx.lock();       //合并完，又回到centralcache，加锁
		}
		start = next;
	}
	_spanLists[index]._mtx.unlock();
}